﻿#region Copyright & License
//
// Copyright 2010 sd&c Hofmann 
// 
// mailto:info@sdch.de
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.XPath;
using log4net;
using log4net.Core;
using log4net.Repository;
using log4net.Config;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint;
using Microsoft.Office.Server.Diagnostics;
using Microsoft.SharePoint.Utilities;
using System.Threading;

namespace SDCH.SharePoint.Log4Sandbox
{
    public class SandBoxLogManager
    {
        private static LoggerConfigurationWatcher _loggerConfigurationWatcher;

        public static ILog GetLogger(Guid pSiteID, string pLoggerName)
        {
            ILog log;
            try
            {
                log = LogManager.GetLogger(pSiteID.ToString(), pLoggerName);
            }
            catch (LogException)
            {
                  log = LogManager.GetLogger(InitializeRepository(pSiteID.ToString()), pLoggerName);
            }

            return log;
        }

        private static string InitializeRepository(string pRepositoryName)
        {
            ILoggerRepository loggerRepository = LogManager.CreateRepository(pRepositoryName);

            SPFarm farm = SPFarm.Local;
            SiteLogRepositoryConfigurationObject repositoryConfiguration = farm.GetChild<SiteLogRepositoryConfigurationObject>(pRepositoryName);

            if (!(_loggerConfigurationWatcher != null))
            {
                _loggerConfigurationWatcher = new LoggerConfigurationWatcher(SPUtility.GetGenericSetupPath("CONFIG\\SDCH.SharePoint.Log4Sandbox") + "\\log4net.config");
            }

            if (!(repositoryConfiguration != null))
            {
                repositoryConfiguration = new SiteLogRepositoryConfigurationObject(pRepositoryName, farm);
            }

            _loggerConfigurationWatcher.ConfigureRepository(repositoryConfiguration);
            repositoryConfiguration.Update(true);

            return loggerRepository.Name;
        }




        internal class LoggerConfigurationWatcher
        {

            /// <summary>
            /// The default amount of time to wait after receiving notification
            /// before reloading the config file.
            /// </summary>
            private const int c_intTimeoutMillis = 500;

            /// <summary>
            /// The timer used to compress the notification events.
            /// </summary>
            private Timer _timerFileChangeEvent;

            /// <summary>
            /// 
            /// </summary>
            private Timer _timerPersistedObjectChangedEvent;

            /// <summary>
            /// 
            /// </summary>
            private FileInfo _fiConfigurationFile;

            private Queue<SiteLogRepositoryConfigurationObject> _queuePersistedObjectID = new Queue<SiteLogRepositoryConfigurationObject>();


            public LoggerConfigurationWatcher(string pstrConfigurationFilePath)
            {
                _fiConfigurationFile = new FileInfo(pstrConfigurationFilePath);
                InitializePersistedObjectWatcher();
                InitializeFileWatcher();
            }

            public void ConfigureRepository(SiteLogRepositoryConfigurationObject pRepositoryConfiguration)
            {
                try
                {
                    ILoggerRepository loggerRepository = LogManager.GetRepository(pRepositoryConfiguration.Name);
                    if (pRepositoryConfiguration.IsCustomConfig)
                    {
                        XmlConfigurator.Configure(loggerRepository, pRepositoryConfiguration.Configuration);
                        PortalLog.DebugLogString(PortalLogLevel.Verbose,
                            "Configure LoggerRepository:{0} with Configuration [{1}]",
                            loggerRepository.Name,
                            pRepositoryConfiguration.Configuration.ToString());
                    }
                    else
                    {
                        XmlConfigurator.Configure(loggerRepository, _fiConfigurationFile);
                        PortalLog.DebugLogString(PortalLogLevel.Verbose,
                            "Configure LoggerRepository:{0} with ConfigurationFile: {1}",
                            loggerRepository.Name,
                            _fiConfigurationFile.FullName);
                    }
                }
                catch
                {

                }
            }

            #region Configuration Persisted Object Watcher

            private void InitializePersistedObjectWatcher()
            {

                SPFarm.ObjectChanged += new SPConfigurationChangeEventHandler(SPFarm_ObjectChanged);
                _timerPersistedObjectChangedEvent = new Timer(new TimerCallback(OnWatchedPersistedObjectChanged), null, Timeout.Infinite, Timeout.Infinite);
            }

            void SPFarm_ObjectChanged(object sender, SPConfigurationChangeEventArgs e)
            {
                try
                {
                    SPFarm farm = (SPFarm)sender;
                    SiteLogRepositoryConfigurationObject repositoryConfiguration = (SiteLogRepositoryConfigurationObject)farm.GetObject(e.ObjectId);
                    if (!_queuePersistedObjectID.Contains(repositoryConfiguration))
                    {
                        _queuePersistedObjectID.Enqueue(repositoryConfiguration);
                    }
                    _timerPersistedObjectChangedEvent.Change(c_intTimeoutMillis, Timeout.Infinite);
                }
                catch { }
            }

            void OnWatchedPersistedObjectChanged(object state)
            {

                lock (_queuePersistedObjectID)
                {
                    while (_queuePersistedObjectID.Count > 0)
                    {
                        SiteLogRepositoryConfigurationObject repositoryConfiguration = _queuePersistedObjectID.Dequeue();
                        ConfigureRepository(repositoryConfiguration);
                    }
                }

            }
            #endregion

            #region Configuration File Watcher

            private void InitializeFileWatcher()
            {
                // Create a new FileSystemWatcher and set its properties.
                FileSystemWatcher watcher = new FileSystemWatcher();

                watcher.Path = _fiConfigurationFile.DirectoryName;
                watcher.Filter = _fiConfigurationFile.Name;

                // Set the notification filters
                watcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite | NotifyFilters.FileName;

                // Add event handlers. OnChanged will do for all event handlers that fire a FileSystemEventArgs
                watcher.Changed += new FileSystemEventHandler(ConfigureAndWatchHandler_OnChanged);


                // Begin watching.
                watcher.EnableRaisingEvents = true;

                // Create the timer that will be used to deliver events. Set as disabled
                _timerFileChangeEvent = new Timer(new TimerCallback(OnWatchedFileChange), null, Timeout.Infinite, Timeout.Infinite);
            }

            private void ConfigureAndWatchHandler_OnChanged(object sender, FileSystemEventArgs args)
            {
                _timerFileChangeEvent.Change(c_intTimeoutMillis, Timeout.Infinite);

            }

            /// <summary>
            /// Called by the timer when the configuration has been updated.
            /// </summary>
            /// <param name="state">null</param>
            private void OnWatchedFileChange(object state)
            {
                SiteLogRepositoryConfigurationObjectCollection colConfig = new SiteLogRepositoryConfigurationObjectCollection(SPFarm.Local);

                foreach (var curConfigObject in colConfig)
                {
                    if (!curConfigObject.IsCustomConfig)
                    {
                        ConfigureRepository(curConfigObject);
                    }
                }

            }

            #endregion

        }
    }
}
